/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.amuthu.parser;

import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.amuthu.DataStore;
import org.amuthu.expression.Expression;
import org.amuthu.expression.binary.ArithmeticExpression;
import org.amuthu.expression.binary.ComparisionExpression;
import org.amuthu.expression.binary.LogicExpression;
import org.amuthu.expression.exception.InvalidArgumentException;
import org.amuthu.expression.function.FunctionEvaluator;
import org.amuthu.expression.function.FunctionExpression;
import org.amuthu.expression.function.FunctionManager;
import org.amuthu.expression.terminal.NumberExpression;
import org.amuthu.expression.terminal.QuoteExpression;
import org.amuthu.expression.unary.UnaryExpression;

/**
 *
 * @author prabhu
 */
public class EvaluationVisitor extends ExpressionVisitor
{
   private double result;
   private DataStore dataStore;
   private HashMap<String, Double> functionValues;

   public double getResult()
   {
      return result;
   }

   public EvaluationVisitor(DataStore dataStore)
   {
      functionValues = new HashMap<String, Double>();
      this.dataStore = dataStore;
   }

   public double getFunctionValue(String name)
   {
      if (functionValues.containsKey(name))
      {
         return functionValues.get(name);
      }
      return 0.0;
   }

   @Override
   public void visit(ComparisionExpression expr)
   {
      expr.getChild(0).accept(this);
      double left = result;

      expr.getChild(1).accept(this);
      double right = result;

      switch (expr.getExprType())
      {
         case EQUALTO:
            result = (left == right) ? 1.0D : 0.0D;
            break;
         case NOT_EQUALTO:
            result = (left != right) ? 1.0D : 0.0D;
            break;
         case LESS_THAN:
            result = (left < right) ? 1.0D : 0.0D;
            break;
         case LESS_THAN_EQUAL:
            result = (left <= right) ? 1.0D : 0.0D;
            break;
         case GREATER_THAN:
            result = (left > right) ? 1.0D : 0.0D;
            break;
         case GREATER_THAN_EQUAL:
            result = (left >= right) ? 1.0D : 0.0D;
            break;
         default:
            throw new AssertionError();
      }
   }

   @Override
   public void visit(LogicExpression expr)
   {
      expr.getChild(0).accept(this);
      double left = result;

      expr.getChild(1).accept(this);
      double right = result;

      switch (expr.getExprType())
      {
         case AND:
            result = (left >= 0.1D && right >= 0.1D) ? 1.0D : 0.0D;
            break;
         case OR:
            result = (left >= 0.1D || right >= 0.1D) ? 1.0D : 0.0D;
            break;
         default:
            throw new AssertionError();
      }
   }

   @Override
   public void visit(NumberExpression expr)
   {
      result = expr.getValue();
   }

   @Override
   public void visit(ArithmeticExpression expr)
   {
      expr.getChild(0).accept(this);
      double left = result;

      expr.getChild(1).accept(this);
      double right = result;

      switch (expr.getExprType())
      {
         case ADD:
            result = left + right;
            break;

         case SUBTRACT:
            result = left - right;
            break;

         case MULTIPLY:
            result = left * right;
            break;

         case DIVIDE:
            if (right != 0.0D)
            {
               result = left / right;
            }
            break;

         case MODULO:
            result = left % right;
            break;

         case POWER:
            result = Math.pow(left, right);
            break;

         default:
            throw new AssertionError();
      }
   }

   @Override
   public void visit(QuoteExpression expr)
   {
      result = 0.0D;

      switch (expr.getQuoteKind())
      {
         case DAY_CLOSE:
            result = dataStore.getClose()[dataStore.getSize() - 1];
            break;

         case DAY_OPEN:
            result = dataStore.getOpen()[dataStore.getSize() - 1];
            break;

         case DAY_HIGH:
            result = dataStore.getHigh()[dataStore.getSize() - 1];
            break;

         case DAY_LOW:
            result = dataStore.getLow()[dataStore.getSize() - 1];
            break;

         case DAY_VOLUME:
            result = dataStore.getVolume()[dataStore.getSize() - 1];
            break;

         default:
            throw new AssertionError();
      }
   }

   @Override
   public void visit(UnaryExpression expr)
   {
      expr.getChild(0).accept(this);

      switch (expr.getExprType())
      {
         case NOT:
            result = (result >= 0.1D) ? 0.0D : 1.0D;
            break;

         case NEGATE:
            result = -1 * result;
            break;

         default:
            throw new AssertionError();
      }
   }

   @Override
   public void visit(FunctionExpression expr)
   {
      FunctionEvaluator evaluator = FunctionManager.getInst().getEvaluator(expr.getName());

      if (evaluator != null)
      {
         try
         {
            result = evaluator.computeIndicator(dataStore, expr.getChildren(), this);
            functionValues.put(expr.getName(), result);
         }
         catch (InvalidArgumentException ex)
         {
            Logger.getLogger(EvaluationVisitor.class.getName()).log(Level.SEVERE, null, ex);
         }
      }
   }

   @Override
   public double evaluate(Expression expr)
   {
      expr.accept(this);

      return result;
   }

}
